package com.unitech.drink.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.unitech.common.enums.BusinessErrorCode;
import com.unitech.common.exception.BusinessException;
import com.unitech.drink.api.beans.DrinkParam;
import com.unitech.drink.api.beans.DrinkSearchParam;
import com.unitech.drink.api.beans.PagedDrinkInfo;
import com.unitech.drink.api.enums.CategoryType;
import com.unitech.drink.constant.ConstantUtil;
import com.unitech.drink.converter.DrinkConverter;
import com.unitech.drink.service.intf.DrinkService;
import com.unitech.drink.dao.entity.Drink;
import com.unitech.drink.api.beans.DrinkInfo;
import com.unitech.drink.dao.DrinkDao;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class DrinkServiceImpl implements DrinkService {
    private static final String CACHE_PREFIX = "drinks:";
    @Autowired
    private RedisTemplate<String, DrinkInfo> redisTemplate;
    @Autowired
    private ConstantUtil constantUtil;

    @Autowired
    private DrinkDao drinkDao;

    @Override
    public DrinkInfo loadDrinkInfo(Long id) {
        // 查看缓存是否命中
        ValueOperations<String, DrinkInfo> operations = redisTemplate.opsForValue();
        String key = CACHE_PREFIX + id;
        // 缓存命中
        if (redisTemplate.hasKey(key)) {
            log.info("Get #{} drink  from redis.", id);
            return operations.get(key);
        }

        // 缓存未命中
        // id是否存在
        Drink drink = this.getDrinkById(id);
        log.info("Found #{} drink {} from mysql.", id, drink.getName());
        DrinkInfo drinkInfo = DrinkConverter.convertToDrinkInfo(drink);
        // 存入redis缓存
        operations.set(key, drinkInfo);
        // 注意：一定要设置缓存过期时间
        redisTemplate.expire(key, 30, TimeUnit.SECONDS);
        log.info("Put #{} drink {} to redis.", id, drinkInfo.getName());

        return drinkInfo;
    }

    @Override
    public PagedDrinkInfo search(DrinkSearchParam request) {
        // 分页
        PageHelper.startPage(request.getPage(), request.getPageSize());
        // 按条件搜索
        Example example = new Example(Drink.class);
        example.createCriteria()
                .andLike("name",
                        StringUtils.hasLength(request.getName())? "%" + request.getName().trim() + "%" : "%%")
                .andEqualTo("categoryId", request.getCategoryId())
                .andBetween("price", request.getMinPrice(), request.getMaxPrice())
                .andBetween("createTime", request.getStartDate(), request.getEndDate());

        List<Drink> drinks = drinkDao.selectByExample(example);
        PageInfo<Drink> result = new PageInfo<>(drinks);
        List<DrinkInfo> drinkInfos = result.getList().stream()
                .map(DrinkConverter::convertToDrinkInfo)
                .collect(Collectors.toList());

        return PagedDrinkInfo.builder()
                .drinks(drinkInfos)
                .page(result.getPageNum())
                .total(result.getTotal())
                .build();
    }

    @Override
    public DrinkInfo createDrink(DrinkParam request) {
        // 不能创建同名的饮品
        Example example = new Example(Drink.class);
        example.createCriteria().andEqualTo("name", request.getName().trim());
        Drink existedDrink = drinkDao.selectByExample(example).stream()
                .findFirst()
                .orElse(null);
        if (existedDrink != null) {
            throw new BusinessException(BusinessErrorCode.DRINK_EXISTS);
        }

        Drink drink = Drink.builder()
                .name(request.getName())
                .price(request.getPrice())
                .categoryId(request.getCategoryId() != null ? request.getCategoryId() : CategoryType.UNKNOWN.getCode())
                .createTime(request.getCreateTime() != null ? request.getCreateTime() : LocalDate.now())
                .imageUrl(request.getImageUrl())
                .build();
        if (drinkDao.insertSelective(drink) != 1) {
            throw new BusinessException(BusinessErrorCode.FAILURE);
        }

        return DrinkConverter.convertToDrinkInfo(drink);
    }

    @Override
    public DrinkInfo updateDrink(DrinkParam request) {
        // id是否存在
        Drink drink = this.getDrinkById(request.getId());
        BeanUtils.copyProperties(request, drink);

        if (drinkDao.updateByPrimaryKeySelective(drink) != 1) {
            throw new BusinessException(BusinessErrorCode.FAILURE);
        }

        // 更新数据后，删除缓存
        deleteCache(drink.getId());

        return DrinkConverter.convertToDrinkInfo(drink);
    }

    private void deleteCache(Long id) {
        // Cache Aside模式
        // 删除缓存
        String key = CACHE_PREFIX + id;
        if (redisTemplate.hasKey(key)) {
            redisTemplate.delete(key);
        }
        log.info("Delete #{} drink from redis.", id);
    }

    @Override
    public void deleteDrink(Long id) {
        Drink drink = drinkDao.selectByPrimaryKey(id);
        if (drinkDao.delete(drink) != 1) {
            throw new BusinessException(BusinessErrorCode.FAILURE);
        }

        // 删除缓存
        deleteCache(id);
    }

    @Override
    public List<DrinkInfo> loadDrinks(List<Long> ids) {
        Example example = new Example(Drink.class);
        example.createCriteria().andIn("id", ids);
        return drinkDao.selectByExample(example).stream()
                .map(d -> DrinkConverter.convertToDrinkInfo(d))
                .collect(Collectors.toList());
    }

    @Override
    public String uploadDrinkImage(MultipartFile file) {
        // 工具类获取值：分别是：地域节点、id、秘钥、项目名称
        String endpoint = constantUtil.getEndpoint();
        String accessKeyId = constantUtil.getKeyid();
        String accessKeySecret = constantUtil.getKeysecret();
        String bucketName = constantUtil.getBucketname();

        try {
            // 创建OSS实例
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            // 上传文件信息
            InputStream inputStream = file.getInputStream();
            String fileName = file.getOriginalFilename();

            // 调用oss方法实现上传
            // 参数一：Bucket名称
            // 参数二：上传到oss文件路径和文件名称
            // 参数三：上传文件的流
            ossClient.putObject(bucketName,fileName,inputStream);

            // 关闭OSSClient
            ossClient.shutdown();

            // 把上传之后的文件路径返回
            // 需要把上传到阿里云路径返回
            String url = " https://"+bucketName+"."+endpoint+"/"+fileName;
            return url;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 通过id查询饮品, 用于检查id是否存在
    private Drink getDrinkById(Long id) {
        Drink drink = drinkDao.selectByPrimaryKey(id);
        if (drink == null) {
            throw new BusinessException(BusinessErrorCode.DRINK_NOT_FOUND);
        }
        return drink;
    }
}
